home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MACD 5
/
MACD 5.bin
/
workbench
/
tools
/
czesc_1
/
dr
/
source
/
purify.a
< prev
next >
Wrap
Text File
|
1994-02-23
|
9KB
|
248 lines
* This here is startup code for Amiga Aztec C programs, to make them pure.
* Assemble it with the command "as purify.a" and link the resulting purify.o
* file with your C program, in some manner like "ln yourprogram.o purify.o -lc",
* and your program can then be marked as pure, and made resident, IF it meets
* certain conditions:
*
* 1) You must not use the large data model for accessing your global variables.
*
* 2) You MUST NOT call the geta4() function from within any part of your program
* that is run outside of your main process. Subtasks, interrupt handlers, and
* wedges installed with SetFunction may not use geta4, and thus may not access
* your global variables by any direct method. Note that #pragma intfunc causes
* an implicit call of geta4().
*
* 3) You must not initialize global variables using the addresses of other
* global variables. For example, the following is not correct:
*
* char foo[100], bar[100];
* char *foobar[2] = { foo, bar }; /* WRONG */
*
* If you do this, then any references to foobar[0] will end up accessing
* DIFFERENT MEMORY than accessing foo directly will. Instead, you have to do
* such initialization in the code, with statements like "foobar[0] = foo;
* foobar[1] = bar;".
*
* 4) You must not use detach.o -- this applies to ALL pure programs, not just
* those that use this startup code.
*
* 5) Do not use this when making a shared library or device driver. ("Duh...")
*
* This was originally based on resident.asm by Olaf Barthel, which is available
* on fish disk 396, but does not contain any of his code. Barthel's version had
* serious errors in it, and will not work in many cases. It was also a lot less
* efficient than the method I use.
*
* With the Aztec assembler, do not use the -N flag (one pass), because there's
* a bug that makes the ENTRY directive not work with that option. It will need
* a few changes in syntax to work with other assemblers. It might be possible
* to make versions for other C compilers, so I've labeled everything which I
* know to be Aztec specific with a comment containing the word "Aztec".
*
* The one possibly not quite cool bit of programming used here is that I used
* SysBase->ThisTask instead of FindTask(0), in several places. If they ever
* make an Amiga that runs tasks on several CPUs at once, that might break.
*
* This is by Paul Kienitz, 1 December 1991, placed in the public domain.
* Revised 11 March 1992 to remove mistake -- DOSBase was accessed after the
* memory it was in was already freed. A couple other little changes. Some
* changes to this comment, but not to the code, were made later.
; We avoid pulling in include files by copying a few definitions here:
MEMB_FAST equ 2 ; exec/memory.i
MEMB_CLEAR equ 16 ; exec/memory.i
ThisTask equ 276 ; exec/execbase.i
AttnFlags equ 296 ; exec/execbase.i
AFB_68881 equ 4 ; exec/execbase.i
AG_OpenLib equ $30000 ; exec/alerts.i
AO_DOSLib equ $8007 ; exec/alerts.i
pr_ReturnAddr equ 176 ; libraries/dosextens.i
pr_CLI equ 172 ; libraries/dosextens.i
pr_MsgPort equ 92 ; libraries/dosextens.i
_LVOTypeOfMem equ -534
_LVOAllocMem equ -198
_LVOOldOpenLibrary equ -408
_LVOAlert equ -108
_LVOSupervisor equ -30
_LVOFreeMem equ -210
_LVOCloseLibrary equ -414
_LVOWaitPort equ -384
_LVOGetMsg equ -372
_LVOReplyMsg equ -378
_LVOForbid equ -132
lcall macro
jsr _LVO\1(a6)
endm
AbsExecBase equ (4).w
xdef .begin,_geta4
xref __main,_SysBase,_DOSBase
xref __savsp ; Aztec
;; xdef entree,cleanup ; for debugging
; the use of the label ".begin" for the entry point is an Aztec-ism
entry .begin
.begin:
entree:
far data ; Aztec
lea __H1_org+32766,a4 ; to access original globals
lea __H1_end,a1 ; Aztec; end of initialized data
lea __H2_org,a2 ; Aztec; start of BSS
near data ; Aztec
cmp.l a1,a2 ; the same address?
bne bomb ; if not, refuse to run
move.l a0,d6 ; save command arg-line pointer
move.l d0,d5 ; save command arg length
lea __H1_org,a1 ; Aztec; start of data hunk
move.l AbsExecBase,a6
lcall TypeOfMem ; what kind of ram is it in?
bclr.l #MEMB_FAST,d0 ; do NOT force fast ram!
bset.l #MEMB_CLEAR,d0 ; do force cleared memory
move.l d0,d1 ; memory type requirements
move.l #(__H2_end-__H1_org),d0 ; Aztec; size of all globals
lcall AllocMem ; space for a complete copy
tst.l d0
beq bomb ; if it failed, don't run
move.l d0,a2
lea __H1_org,a0 ; Aztec; address of old globals
move.l a2,a1 ; address of new globals
move.l #((__H1_end-__H1_org)/4)-1,d0 ; Aztec; size to copy
copyit: move.l (a0)+,(a1)+ ; copy all initialized data
dbra d0,copyit
* Here's where we get tricky. We need to put the address of our dublicated
* global variable space someplace where it can be found by geta4(), and we need
* to make sure that we free the copied globals no matter how the program exits.
* What we do is save the pointer on the stack, and then make it appear that the
* final exit address that the program must return to is the address of our
* cleanup routine. To do this, after pushing the pointer to the copied globals,
* we push the size of the remaining available stack (12 bytes less than the
* original size) and then the address of the cleanup code. We set the variable
* __savsp to point to that cleanup code address, so that the Aztec exit()
* function will return to that point. We set the pr_ReturnAddr field of our
* struct Process to point one longword above that, to make the dos.library
* Exit() function return to the cleanup code. This is also used by geta4() to
* locate the saved pointer to the copied globals.
move.l a2,-(sp) ; where geta4() can find it
move.l 8(sp),a0 ; original size of the stack
sub.w #12,a0 ; minus space we're using
move.l a0,-(sp) ; make stack look smaller
move.l ThisTask(a6),a0 ; equivalent to FindTask(0)
move.l sp,pr_ReturnAddr(a0) ; for geta4() and dos Exit()
pea cleanup ; so everybody goes there
* The stack now looks like this:
*
* Stack size in bytes <- old pr_ReturnAddr pointed here
* Return address for final exit <- __savsp would have pointed here
* Pointer to globals for geta4 <- also used by cleanup
* New stack size, minus twelve <- new pr_ReturnAddr points here
* Fake return address: cleanup <- __savsp will point here
*
* Once __main is called, there will also be these:
*
* Command line arg address <- argument to __main
* Command line arg length <- argument to __main ...SP is now here
* Return address to this module <- falls through to cleanup
bsr _geta4 ; use new globals
move.l sp,__savsp ; Aztec; exit() and stack check
move.l a6,_SysBase ; set up base for C program
lea dosname,a1
lcall OldOpenLibrary ; open dos.library
move.l d0,_DOSBase ; set up another base
bne dosokay
move.l #AG_OpenLib!AO_DOSLib,d7 ; recoverable - no dos
lea ThisTask(a6),a5 ; make it show our task adr
lcall Alert ; frighten the user
moveq #127,d0 ; return value
rts ; "returns" to cleanup
dosokay: btst.b #AFB_68881,AttnFlags+1(a6) ; is there an FPU?
beq nofpu
lea resetfpu,a5
lcall Supervisor ; reset it in supervisor mode
nofpu: movem.l d5/d6,-(sp) ; pass arguments to _main()
jsr __main ; RUN THE PROGRAM!
add.w #12,sp ; simulate rts to cleanup
cleanup: move.l d0,d7 ; set aside the return value
bsr _geta4 ; so we can check DOSBase
move.l AbsExecBase,a6
move.l _DOSBase,a1
move.l a1,d0 ; fake tst.l a1
beq dosneveropen ; was there ever a DOSBase?
lcall CloseLibrary ; if yes, close it
dosneveropen: move.l 4(sp),a1 ; address of copied globals
move.l #(__H2_end-__H1_org),d0 ; size of copied globals
lcall FreeMem ; get rid of the copy
addq #8,sp ; restore primordial stack
move.l ThisTask(a6),a1
lea 4(sp),a0 ; just to be paranoid...
move.l a0,pr_ReturnAddr(a1) ; restore old pr_ReturnAddr
move.l d7,d0 ; restore the return value
rts ; goodbye cruel world
bomb: move.l AbsExecBase,a6 ; come here if we can't run
move.l ThisTask(a6),a0
tst.l pr_CLI(a0) ; are we a Workbench process?
bne noworkbench ; if yes, then we need to
; reply the startup message:
lea pr_MsgPort(a0),a0
move.l a0,a3 ; remember our msgport address
lcall WaitPort ; here come de message
move.l a3,a0
lcall GetMsg ; got it
move.l d0,a3 ; yes, remember it
lcall Forbid ; standard shutdown procedure
move.l a3,a1
lcall ReplyMsg ; tell WB to unload us
noworkbench: moveq #127,d0 ; largest convenient return code
rts ; might return to cleanup
mc68881 ; Aztec; activate FPU instruction opcodes
resetfpu: clr.l -(sp) ; call this in supervisor mode
frestore (sp)+ ; reset the FPU
rte ; return to user mode
* Here's our magic routine to set up a4 for accessing the copied globals.
* CALL ONLY FROM WITHIN THE SAME PROCESS.
_geta4: move.l AbsExecBase,a4
move.l ThisTask(a4),a4
move.l pr_ReturnAddr(a4),a4 ; ptr to FAKE stack size
move.l 4(a4),a4 ; stored ptr to copied globals
lea 32766(a4),a4 ; add the offset
rts
dosname: dc.b 'dos.library',0 ; for OldOpenLibrary
dseg ; Aztec
; Assembler bug: If you declare __H1_org public within the code segment,
; then the assembler refers to it with small data even if you have told
; it to use large data. Hence the above dseg is necessary. This does not seem
; to happen with ordinary symbols, just the __Hx_xxx ones.
public __H1_org,__H1_end,__H2_org,__H2_end ; Aztec